package com.central.apps.service.impl;

import com.central.app.service.HandlerService;
import com.central.apps.entity.MpxjTaskBean;
import com.central.apps.service.IGanttService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;

@Slf4j
@Service
public class GanttServiceImpl implements IGanttService {
    @Autowired
    private HandlerService handlerService;

    /**
     * 计算甘特图关键路径
     *
     * @param proid
     * @return List<MpxjTaskBean>
     */
    @Override
    public LinkedList<MpxjTaskBean> computeCriticalPath(Long proid) throws Exception {
        // 查询项目计划任务信息
        List<Map<String, Object>> taskList = handlerService.findBySql("select * from gq_scheduleplantask where proid = " + proid + " order by cast(recordId as SIGNED)");

        if (CollectionUtils.isEmpty(taskList)) {
            return null;
        }

        // 1.初始化关键路径数据
        LinkedList<MpxjTaskBean> initTaskList = new LinkedList<>();
        for (Map<String, Object> taskMap : taskList) {
            initTaskList.add(convertInfo(taskMap));
        }

        // 2.回流算法:有些任务的关键成本没有计算出来
        computeCriticalCost(initTaskList);

        // 3.计算最大成本
        computeMaxCost(initTaskList);

        // 4.组织没有前置任务的任务集合
        LinkedList<MpxjTaskBean> tempList = initials(initTaskList);

        // 5.计算最早开始
        calculateEarly(tempList, initTaskList);

        // 6.计算关键路径任务集合
        return calculateCritical(initTaskList);
    }

    /**
     * 计算甘特图进度加权平均算法
     *
     * @param taskid
     */
    @Override
    public void computeWeightedAverage(Long taskid, String tableName) throws Exception {
        // 1.查询任务信息
        List<Map<String, Object>> taskList = handlerService.findBySql("select * from " + tableName + " where id = " + taskid);
        Map<String, Object> taskMap = taskList.get(0);
        Integer proId = MapUtils.getInteger(taskMap, "proid");
        Integer parentId = MapUtils.getInteger(taskMap, "parentId");

        if (parentId == 0) {
            return;
        }

        // 2.递归计算进度
        recursionCalcSchedule(parentId, tableName);
    }

    /**
     * 递归计算进度
     *
     * @param parentId
     */
    private void recursionCalcSchedule(Integer parentId, String tableName) throws Exception {
        // 查询父级任务信息
        Map<String, Object> parentTaskMap = handlerService.findOneBySql("select * from " + tableName + " where id = " + parentId);

        if (parentTaskMap == null || parentTaskMap.isEmpty()) {
            return;
        }

        // 查询父级任务对应所有下级任务信息
        List<Map<String, Object>> childTaskList = handlerService.findBySql("select * from " + tableName + " where parentId = " + parentId);
        BigDecimal totalDuration = BigDecimal.ZERO;
        BigDecimal totalScheduleDuration = BigDecimal.ZERO;
        for (Map<String, Object> childTaskMap : childTaskList) {
            Integer progress = (Integer) childTaskMap.get("progress");
            Integer hasChild = (Integer) childTaskMap.get("hasChild");
            Integer duration = 0;

            if (hasChild == 1) {
                Integer childTaskId = (Integer) childTaskMap.get("id");
                duration = recursionCalcDuration(childTaskId, tableName);
            } else {
                duration = (Integer) childTaskMap.get("duration");
            }

            String durationTimeUnit = (String) childTaskMap.get("durationTimeUnit");

            totalDuration = totalDuration.add(BigDecimal.valueOf(duration));

            BigDecimal scheduleDuration = BigDecimal.valueOf(progress).multiply(BigDecimal.valueOf(duration))
                    .divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP);
            totalScheduleDuration = totalScheduleDuration.add(scheduleDuration);
        }

        if (totalDuration.compareTo(BigDecimal.ZERO) == 0) {
            return;
        }

        // 计算父级任务完成百分比
        BigDecimal pencent = totalScheduleDuration.multiply(BigDecimal.valueOf(100)).divide(totalDuration,0, BigDecimal.ROUND_HALF_UP);

        // 更新父级任务进度信息
        handlerService.updateBySql("update " + tableName + " set progress = " + pencent + ", unfinishprocess = 100 - progress where id = " + parentId);
        parentId = (Integer) parentTaskMap.get("parentId");

        if (parentId > 0) {
            recursionCalcSchedule(parentId, tableName);
        }
    }

    /**
     * 递归计算子级总工时
     *
     * @param parentTaskId
     * @return
     */
    private Integer recursionCalcDuration(Integer parentTaskId, String tableName) {
        // 查询父级任务对应所有下级任务信息
        List<Map<String, Object>> childTaskList = handlerService.findBySql("select * from " + tableName + " where parentId = " + parentTaskId);
        BigDecimal totalDuration = BigDecimal.ZERO;
        for (Map<String, Object> childTaskMap : childTaskList) {
            Integer hasChild = (Integer) childTaskMap.get("hasChild");
            Integer duration = 0;

            if (hasChild == 1) {
                Integer childTaskId = (Integer) childTaskMap.get("id");
                duration = recursionCalcDuration(childTaskId, tableName);
            } else {
                duration = (Integer) childTaskMap.get("duration");
            }
            totalDuration = totalDuration.add(BigDecimal.valueOf(duration));
        }

        return totalDuration.intValue();
    }

    /**
     * 转换任务信息
     *
     * @param taskMap
     * @return MpxjTaskBean
     */
    private MpxjTaskBean convertInfo(Map<String, Object> taskMap) {
        MpxjTaskBean taskBean = new MpxjTaskBean();
        taskBean.setId(String.valueOf((Integer) taskMap.get("id")));
        taskBean.setDuration((Integer) taskMap.get("duration"));
        taskBean.setParentId(String.valueOf((Integer) taskMap.get("parentId")));
        taskBean.setProgress((Integer) taskMap.get("progress"));
        taskBean.setDepends((String) taskMap.get("predecessors"));
        taskBean.setCritical(false);
        taskBean.setEarlyStart(-1);
        taskBean.setEarlyFinish(-1);
        taskBean.setLatestStart(-1);
        taskBean.setLatestFinish(-1);
        taskBean.setCriticalCost(-1);

        return taskBean;
    }

    /**
     * 计算任务的关键成本
     *
     * @param initTaskList
     */
    private void computeCriticalCost(LinkedList<MpxjTaskBean> initTaskList) {
        HashSet completed = new HashSet();
        HashSet remaining = new HashSet();
        // 需要计算成本的任务
        for (MpxjTaskBean initTask : initTaskList) {
            remaining.add(initTask);
        }

        //回流算法：有些任务的关键成本没有计算出来
        while (!remaining.isEmpty()) {
            boolean progress = false;
            // 循环计算任务最大成本
            for (Iterator it = remaining.iterator(); it.hasNext(); ) {
                MpxjTaskBean taskBean = (MpxjTaskBean) it.next();

                HashSet depend = new HashSet();
                if (StringUtils.isNotEmpty(taskBean.getDepends())) {
                    String[] dependArray = taskBean.getDepends().split(",");

                    for (String str : dependArray) {
                        for (MpxjTaskBean initTask : initTaskList) {
                            if (str.equals(initTask.getId())) {
                                depend.add(initTask);
                                break;
                            }
                        }
                    }
                }
                if (completed.containsAll(depend)) {
                    // 计算所有依赖项，关键成本为最大依赖项关键成本加上本身的成本
                    Integer critical = 0;

                    for (Object d : depend) {
                        MpxjTaskBean tb = (MpxjTaskBean) d;
                        if (tb.getCriticalCost() > critical) {
                            critical = tb.getCriticalCost();
                        }
                    }

                    // 将任务设置为计算值
                    taskBean.setCriticalCost(critical + taskBean.getDuration());

                    // 设置任务初始集合
                    int index = initTaskList.indexOf(taskBean);
                    if (index > -1) {
                        MpxjTaskBean initTask = initTaskList.get(index);
                        initTask.setCriticalCost(taskBean.getCriticalCost());
                    }

                    // 删除任务
                    completed.add(taskBean);
                    it.remove();

                    // 标记循环继续
                    progress = true;
                }
            }

            //如果我们没有取得任何进展，那么一定存在一个循环, 我们无法计算关键路径
            if (!progress) {
                throw new RuntimeException("循环依赖，算法已停止！");
            }
        }
    }

    /**
     * 计算最早开始、最早结束、最晚开始、最晚结束
     *
     * @param initTaskList
     */
    private void computeMaxCost(LinkedList<MpxjTaskBean> initTaskList) {
        Integer max = -1;
        for (MpxjTaskBean initTaskBean : initTaskList) {
            if (!"0".equals(initTaskBean.getParentId()) && initTaskBean.getCriticalCost() > max) {
                max = initTaskBean.getCriticalCost();
            }
        }
        for (MpxjTaskBean initTaskBean : initTaskList) {
            initTaskBean.setLatestStart(max - initTaskBean.getCriticalCost());
            initTaskBean.setLatestFinish(initTaskBean.getLatestStart() + initTaskBean.getDuration());
        }
    }

    /**
     * 组织没有前置任务的任务集合
     *
     * @param initTaskList
     * @return LinkedList<MpxjTaskBean>
     */
    private LinkedList<MpxjTaskBean> initials(LinkedList<MpxjTaskBean> initTaskList) {
        LinkedList<MpxjTaskBean> noDependsList = new LinkedList<>();

        for (MpxjTaskBean initTaskBean : initTaskList) {
            if (!"0".equals(initTaskBean.getParentId())) {
                noDependsList.add(initTaskBean);
            }
        }

        return noDependsList;
    }

    /**
     * 计算最早开始、最早结束
     *
     * @param taskList
     * @param initTaskList
     */
    private void calculateEarly(LinkedList<MpxjTaskBean> taskList, LinkedList<MpxjTaskBean> initTaskList) {
        for (int i = 0; i < taskList.size(); i++) {
            MpxjTaskBean taskBean = taskList.get(i);

            int index = initTaskList.indexOf(taskBean);
            if (index > -1) {
                MpxjTaskBean initTask = initTaskList.get(index);
                initTask.setEarlyStart(0);
                initTask.setEarlyFinish(initTask.getDuration());
            }

            taskBean.setEarlyStart(0);
            taskBean.setEarlyFinish(taskBean.getDuration());

            // 设置依赖任务最早开始
            setEarly(taskBean, initTaskList);
        }
    }

    /**
     * 设置依赖任务最早开始、最早结束
     *
     * @param taskBean
     * @param initTaskList
     */
    private void setEarly(MpxjTaskBean taskBean, LinkedList<MpxjTaskBean> initTaskList) {
        Integer completionTime = taskBean.getEarlyFinish();

        // 筛选依赖任务集合
        LinkedList<MpxjTaskBean> dependTaskList = new LinkedList<>();
        for (MpxjTaskBean initTaskBean : initTaskList) {
            if (initTaskBean.getDepends().contains(taskBean.getId())) {
                if (completionTime >= initTaskBean.getEarlyStart()) {
                    initTaskBean.setEarlyStart(completionTime);
                    initTaskBean.setEarlyFinish(completionTime + initTaskBean.getDuration());
                }

                setEarly(initTaskBean, initTaskList);
            }
        }
    }

    /**
     * 计算关键路径数据
     *
     * @param initTaskList
     */
    private LinkedList<MpxjTaskBean> calculateCritical(LinkedList<MpxjTaskBean> initTaskList) {
        if (CollectionUtils.isEmpty(initTaskList)) {
            return null;
        }

        // 计算关键路径任务集合
        LinkedList<MpxjTaskBean> criticalTaskList = new LinkedList<>();
        for (MpxjTaskBean initTaskBean : initTaskList) {
            if (!"0".equals(initTaskBean.getParentId()) && initTaskBean.getEarlyStart().equals(initTaskBean.getLatestStart())) {
                initTaskBean.setCritical(true);
                criticalTaskList.add(initTaskBean);
            }
        }

        return criticalTaskList;
    }
}
